using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Web;
using System.Web.UI;

namespace AjaxControlToolkit
{
	public class ScriptComponentBase : Control, IScriptControl
	{
		#region Protected methods

		/// <summary>
		/// The script type to use for the ScriptControl
		/// </summary>
		protected virtual string ClientControlType
		{
			get
			{
				var attr = (ClientScriptResourceAttribute)TypeDescriptor.GetAttributes(this)[typeof(ClientScriptResourceAttribute)];
				return attr.ComponentType;
			}
		}

		/// <summary>
		/// Fires the PreRender event
		/// </summary>
		/// <param name="e"></param>
		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			EnsureID();

			EnsureScriptManager();

			ScriptManager.RegisterScriptControl(this);
		}

		/// <summary>
		/// 
		/// </summary>
		/// <param name="writer"></param>
		protected override void Render(HtmlTextWriter writer)
		{
			base.Render(writer);

			Page.VerifyRenderingInServerForm(this);

			if (!DesignMode)
				ScriptManager.RegisterScriptDescriptors(this);
		}

		/// <summary>
		/// Describes the settings for this control.
		/// </summary>
		/// <param name="descriptor"></param>
		protected virtual void DescribeComponent(ScriptComponentDescriptor descriptor)
		{
			try
			{
				_renderingScript = true;
				ScriptObjectBuilder.DescribeComponent(this, descriptor, this, null);
			}
			finally
			{
				_renderingScript = false;
			}
		}
		#endregion

		#region Private methods

		/// <summary>
		/// Ensures a ScriptManager exists on the Page for this Control
		/// </summary>
		private void EnsureScriptManager()
		{
			if (_scriptManager == null)
			{
				_scriptManager = ScriptManager.GetCurrent(Page);
				if (_scriptManager == null)
				{
					throw new HttpException(Resources.E_NoScriptManager);
				}
			}
		}

		#endregion

		#region Implementation of IScriptControl

		/// <summary>
		/// Gets a collection of script descriptors that represent ECMAScript (JavaScript) client components.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerable" /> collection of <see cref="T:System.Web.UI.ScriptDescriptor" /> objects.
		/// </returns>
		public IEnumerable<ScriptDescriptor> GetScriptDescriptors()
		{
			if (!Visible) return null;

			EnsureID();

			// store descriptors for this object
			var descriptors = new List<ScriptDescriptor>();

			// build the default description
			var descriptor = new ScriptComponentDescriptor(ClientControlType) {ID = ID};
			DescribeComponent(descriptor);
			descriptors.Add(descriptor);

			// return the description
			return descriptors;
		}

		/// <summary>
		/// Gets a collection of <see cref="T:System.Web.UI.ScriptReference" /> objects that define script resources that the control requires.
		/// </summary>
		/// <returns>
		/// An <see cref="T:System.Collections.IEnumerable" /> collection of <see cref="T:System.Web.UI.ScriptReference" /> objects.
		/// </returns>
		public IEnumerable<ScriptReference> GetScriptReferences()
		{
			if (!Visible) return null;

			var refs = new List<ScriptReference>();
			refs.AddRange(ScriptObjectBuilder.GetScriptReferences(GetType()));
			if (ScriptPath.Length > 0)
			{
				refs.Add(new ScriptReference(ScriptPath));
			}
			return refs;
		}

		#endregion

		#region Public properties

		/// <summary>
		/// For debugging - setting this causes the extender to load the specified script instead of the one out of the resources.  This
		/// lets you set breakpoints and modify the script without rebuilding, etc.
		/// </summary>
		/// <remarks>
		/// Note to inheritors: If you do not wish the user to set the script path, override script path and throw a NotSupportedException on set.  Also decorate the ScriptPath attribute with a [Browsable(false)] and [EditorBrowsableState(EditorBrowsableState.Never)]
		/// </remarks>
		[DefaultValue("")]
		public virtual string ScriptPath
		{
			get { return (string)(ViewState["ScriptPath"] ?? string.Empty); }
			set { ViewState["ScriptPath"] = value; }
		}

		#endregion

		#region Protected properties

		/// <summary>
		/// Gets the ScriptManager for the page
		/// </summary>
		protected ScriptManager ScriptManager
		{
			get
			{
				EnsureScriptManager();
				return _scriptManager;
			}
		}

		/// <summary>
		/// Property for determining if script rendering is currently underway.  This is used to determine
		/// if Script or markup is being rendered.
		/// </summary>
		protected bool IsRenderingScript
		{
			get
			{
				return _renderingScript;
			}
		}

		#endregion

		#region Private fields

		/// <summary>
		/// 
		/// </summary>
		private ScriptManager _scriptManager;

		/// <summary>
		/// 
		/// </summary>
		private bool _renderingScript;

		#endregion
	}
}